	#
	#  This file implements low level functions
	#
	#  Copyright (C) 2002 by Francesco Frigo 
	#
	#  Tuesday 27th September, 2002
	# 
	#  Updated: Friday 26th December, 2003
	#

.section .text


	# Set data segments to a specified value
.global set_data_segments
set_data_segments:
	pushl	%ebp
	movl	%esp,%ebp
	pushw	%ax
	movw	8(%ebp),%ax
	movw	%ax,%ds
	movw	%ax,%es
	movw	%ax,%fs
	movw	%ax,%gs
	movw	%ax,%ss
	popw	%ax
	movl	%ebp,%esp
	popl	%ebp		
	ret

	
	# Halt the PC
.global halt	
halt:
  	cli
	hlt
	# jmp	.		# This would be too CPU consuming
	jmp	halt		# Just in case you were to wake up again... :^)

	# Reset the PC
.global reset	
reset:				# This is the hard method
				# it'd be preferable to use the Keyboard controller to reset the CPU 
	cli			# don't interrupt this tiny code... till I issue an INT 3
	lidt	1f	
	int	$0x03		# Whatever happens the CPU won't like it...
	
1:	.word 0,0
#	nop			# to syncronize disassembler
	
	# Clear all-purpose registers 
.global clear_registers
clear_registers:	
	xorl	%eax,%eax
	xorl	%ebx,%ebx
	xorl	%ecx,%ecx
	xorl	%edx,%edx
	xorl	%esi,%esi
	xorl	%edi,%edi	
	ret		
	
	# Issue an IRET returning from an interrupt
.global issue_iret
issue_iret:
	movl	%eax,1f
	popl	%eax
	movl	%ebp,%esp
	popl	%ebp
	movl	1f,%eax
	iret
	
1:	.long 0
#	nop			# to syncronize disassembler

	# Debug function (ie will be removed sometime)	
	# Test routine to execute test assembly code
.global asm_run
asm_run:
	pushl	%ebp
	movl	%esp,%ebp

	int	$0x20	

	movl	%ebp,%esp		
	popl	%ebp
	ret

# The rest comes from the old pmode_asm.asm


	# Read MSW - "Machine Status Word" register
.global read_msw
read_msw:
        smswl   %eax
        ret

	# Read cr0 - "Control Register 0" and store it into eax
.global read_cr0
read_cr0:
        movl	%cr0,%eax       # read CR0 and store it in to eax
        ret

	
	# Write cr0 - "Control Register 0" from eax
.global write_cr0	
write_cr0:
        pushl	%ebp
        movl	%esp,%ebp
        movl	8(%ebp),%eax	# eax = first parameter (dword_t value)
        movl	%eax,%cr0
	movl	%ebp,%esp
        popl	%ebp
        ret

	# Read cr2 - "Control Register 2" and store it into eax
.global read_cr2
read_cr2:
        movl	%cr2,%eax       # read CR2 and store it in to eax
        ret

	
	# Write cr2 - "Control Register 2" from eax
.global write_cr2
write_cr2:
        pushl	%ebp
	movl	%esp,%ebp
        movl	8(%ebp),%eax	# eax = first parameter (dword_t value)
        movl	%eax,%cr2
        movl	%ebp,%esp
        popl	%ebp
        ret

	# Read cr3 (PDBR) - "Control Register 3" and store it into eax
.global read_cr3
read_cr3:
        mov	%cr3,%eax	# read CR3 and store it in to eax
        ret

	
	# Write cr3 (PDBR) - "Control Register 3" from eax
.global write_cr3
write_cr3:
        pushl	%ebp
	movl	%esp,%ebp
        movl	8(%ebp),%eax	# eax = first parameter (dword_t value)
        movl	%eax,%cr3
        movl	%ebp,%esp
        popl	%ebp
        ret

			
	# Load the GDTR - "Global Descriptor Table" register
.global load_gdt	
load_gdt:
        pushl	%ebp
	movl	%esp,%ebp
        pushl	%ebx
        movl	%ss:8(%ebp),%ebx	# ds:ebx = pointer to GDTR_T structure
        lgdtl	%ds:(%ebx)		# load GDTR
        popl	%ebx
#	lgdt	8(%ebp)
        movl	%ebp,%esp
        popl	%ebp
        ret

	
	# Load the IDTR - "Interrupt Descriptor Table" register
.global load_idt	
load_idt:
        pushl	%ebp
	movl	%esp,%ebp
        pushl	%ebx
        movl	%ss:8(%ebp),%ebx	# ds:ebx = pointer to IDTR_T structure
        lidtl	%ds:(%ebx)		# load IDTR
        popl	%ebx
#	lidt	8(%ebp)
        movl	%ebp,%esp
        popl	%ebp
        ret

	# Load the LDTR - "Local Descriptor Table" register
.global load_ldt
load_ldt:
        pushl	%ebp
	movl	%esp,%ebp
        movw	%ss:8(%ebp),%ax
        lldt	%ax		# load LDTR
        movl	%ebp,%esp
        popl	%ebp
        ret

	
	# Load the TR "Task Register"
.global load_tr	
load_tr:
	pushl	%ebp
	movl	%esp,%ebp
        ltr	8(%ebp)		# load TR (word)
        movl	%ebp,%esp
        popl	%ebp
        ret

	
	# Store the TR
.global store_tr	
store_tr:
        str	%ax
        ret

	
	# Clear the Task Switched flag
.global clear_ts	
clear_ts:
        clts
        ret

	
	# Clear the Direction flag
.global clear_df	
clear_df:
        cld
        ret

	
	# Clear the Interrupt flag
.global clear_if	
clear_if:
        cli
        ret

	
	# Clear the Carry flag
.global clear_cf		
clear_cf:
        clc
        ret

	
	# Set the Direction flag
.global clear_df		
set_df:
        std
        ret

	
	# Set the Interrupt flag
.global set_if	
set_if:
        sti
	ret

	
	# Set the Carry flag
.global set_cf		
set_cf:
        stc
        ret

	
	# Jump to a TSS - "Task State Segment"
.global jump_tss	
jump_tss:
	pushl	%ebp
	movl	%esp,%ebp
	# jmp	dword [ss:ebp+8]	; [EBP+8] -> TSS descriptor; the jump will cause a task switch (offset is ignored)
	jmp	*8(%ebp)
	movl	%ebp,%esp
        popl	%ebp

	
	# Call a TSS - "Task State Segment"
.global call_tss
call_tss:
	pushl	%ebp
	movl	%esp,%ebp
	# call	dword [ss:ebp+8]	; [EBP+8] -> TSS descriptor; the call will cause a task switch (offset is ignored)
	call	*8(%ebp)
	movl	%ebp,%esp
        popl	%ebp

		
	# Invalidate caches
.global invalidate_cache
invalidate_cache:
	invd
	ret

	
	# Write-back and invalidate caches
.global writeback_invalidate_cache
writeback_invalidate_cache:	
	wbinvd
	ret


	# Invalidate TLB entry
.global invalidate_tlb_entry
invalidate_tlb_entry:
	pushl	%ebp
	movl	%esp,%ebp
	
	invlpg	8(%ebp)

	movl	%ebp,%esp
        popl	%ebp
	ret
	
	# Enable Paging
.global paging_enable_asm
paging_enable_asm:
	pushl	%eax
		
	cli
	clts	

	movl	%cr0,%eax
	orl	$0x80000000,%eax
	movl	%eax,%cr0

	ljmp	$0x08,$1f
1:	
	# Load data segment registers
	movw	$0x10,%ax
	movw	%ax,%ds
	movw	%ax,%es
	movw	%ax,%fs
	movw	%ax,%gs
	movw	%ax,%ss

	# 	wbinvd

2:
	popl	%eax
	ret
	

	# Get Interupt Flag status
.global read_if
read_if:
	pushfl
	popl	%eax
	shrl	$9,%eax
	andl	$1,%eax
	ret
